﻿/*                 -*- mode: c; tab-width: 4; indent-tabs-mode: t; -*- */
/* SCE CONFIDENTIAL
 $PSLibId$
 *
 *      Copyright (C) 2010 Sony Computer Entertainment Inc.
 *                        All Rights Reserved.
 *
 */

#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "event.h"

/********************************************************************************************/
/*                                prototypes                                                */
/********************************************************************************************/
static void dumpEventElem(TouchEventElem* pEventElem);
static int pushQueue(TouchEventElem **ppTop, TouchEventElem **ppElem, SceUInt bMerge);
static TouchEventElem* popQueue(TouchEventElem **ppTop);
static TouchEventElem* removeElem(TouchEventElem **ppTop, SceUInt8 id);
static inline SceUInt64 getDelta(SceUInt64 cur, SceUInt64 prev);

/********************************************************************************************/
/*                                functions                                                 */
/********************************************************************************************/
void dumpEventElem(TouchEventElem* pEventElem)
{
	printf("ID:%3d (%s) Pos(%4d,%4d) Delta(%4d,%4d) F(%3d) DF(%3d) DT:%lld\n",
			pEventElem->id,
			((pEventElem->event == TOUCH_EVENT_PRESS) ? "P"  :
			 (pEventElem->event == TOUCH_EVENT_HOLD)  ? "H"  :
			 (pEventElem->event == TOUCH_EVENT_MOVE)  ? "M"  :
			 (pEventElem->event == TOUCH_EVENT_ABORT) ? "A"  :
			 (pEventElem->event == TOUCH_EVENT_ABORT_AND_RELEASE) ? "AR"  : "R"),
			 pEventElem->curX,   pEventElem->curY,
			 pEventElem->deltaX, pEventElem->deltaY,
			 pEventElem->curF,   pEventElem->deltaF,
			 pEventElem->deltaT);
}

int pushQueue(TouchEventElem **ppTop, TouchEventElem **ppElem, SceUInt bMerge)
{
	TouchEventElem *pTemp;

	if(!bMerge){
		(*ppElem)->pNext = NULL;
	}

	if(!*ppTop){
		*ppTop = *ppElem;
		return(0);
	}
	pTemp = *ppTop;

	while(pTemp->pNext){
		pTemp = pTemp->pNext;
	}
	pTemp->pNext = *ppElem;

	return(0);
}

TouchEventElem* popQueue(TouchEventElem **ppTop)
{
	TouchEventElem *pTemp = NULL;

	if(*ppTop){
		pTemp = *ppTop;
		*ppTop = (*ppTop)->pNext;
		pTemp->pNext = NULL;
	}

	return(pTemp);
}

TouchEventElem* removeElem(TouchEventElem **ppTop, SceUInt8 id)
{
	TouchEventElem *pPrev = NULL;
	TouchEventElem *pTemp = NULL;

	if(!*ppTop){
		return(NULL);
	}

	pTemp = *ppTop;
	pPrev = NULL;

	while(pTemp){
		if(pTemp->id == id){
			if(pPrev){
				pPrev->pNext = pTemp->pNext;
			}else{
				*ppTop = pTemp->pNext;
			}
			pTemp->pNext = NULL;
			return(pTemp);
		}
		pPrev = pTemp;
		pTemp = pTemp->pNext;
	}

	return(NULL);
}

static inline SceUInt64 getDelta(SceUInt64 cur, SceUInt64 prev)
{
	SceUInt64 delta = 0;
	if(cur > prev){
		delta = cur - prev;
	}else{
		delta = UINT64_MAX - prev + cur + 1;
	}
	return(delta);
}

int touchEventInit(TouchEventInfo* pEventInfo)
{
	int i;
	TouchEventElem* pElem;

	if(!pEventInfo){
		return(-1);
	}
	memset(pEventInfo, 0, sizeof(TouchEventInfo));

	pEventInfo->moveThreshold = MOVE_THRESHOLD;

	for(i = 0; i < MAX_EVENT; i++){
		pElem = &pEventInfo->eventElem[i];
		/*E initialize event queue */
		pushQueue(&pEventInfo->pFree, &pElem, 0);
	}
	return(0);
}

int touchEventSetMoveThreshold(TouchEventInfo* pEventInfo, SceUInt32 threshold)
{
	if(!pEventInfo){
		return(-1);
	}
	pEventInfo->moveThreshold = threshold;

	return(0);
}


int touchEventUpdate(const SceTouchData* pData, TouchEventInfo* pEventInfo)
{
	TouchEventElem* pTemp;
	TouchEventElem* pPress  = NULL;
	TouchEventElem* pMove   = NULL;
	TouchEventElem* pHold   = NULL;
	TouchEventElem* pAbort  = NULL;
	SceUInt i;

	if(!pEventInfo || !pData){
		return(-1);
	}

	if(pEventInfo->pRelease){
		/*E Push the previous "RELEASE" element to free queue. */
		pushQueue(&pEventInfo->pFree, &pEventInfo->pRelease, 1);
		pEventInfo->pRelease = NULL;
	}

	if(pEventInfo->pPress){
		/*E Merge the previous "PRESS" element to "HOLD" queue. */
		pushQueue(&pEventInfo->pHold, &pEventInfo->pPress, 1);
		pEventInfo->pPress = NULL;
	}
	if(pEventInfo->pMove){
		/*E Merge the previous "MOVE" element to "HOLD" queue. */
		pushQueue(&pEventInfo->pHold, &pEventInfo->pMove, 1);
		pEventInfo->pMove = NULL;
	}
	if(pEventInfo->pAbort){
		/*E Merge the previous "ABORT" element to "HOLD" queue. */
		pushQueue(&pEventInfo->pHold, &pEventInfo->pAbort, 1);
		pEventInfo->pAbort = NULL;
	}

	for(i =0; i < pData->reportNum; i++){
		pTemp = removeElem(&pEventInfo->pHold, pData->report[i].id);
		if(pTemp){
			/*E This touch is already pressed. */
			pTemp->deltaX    = pData->report[i].x - pTemp->curX;
			pTemp->deltaY    = pData->report[i].y - pTemp->curY;
			pTemp->deltaF    = pData->report[i].force - pTemp->curF;
			pTemp->curX      = pData->report[i].x;
			pTemp->curY      = pData->report[i].y;
			pTemp->curF      = pData->report[i].force;
			pTemp->deltaT    = getDelta(pData->timeStamp, pEventInfo->updateTime);
			pTemp->elapsedT += pTemp->deltaT;
			pTemp->pNext     = NULL;

			if((pData->status         & SCE_TOUCH_STATUS_INTERCEPTED) ||
			   (pData->report[i].info & SCE_TOUCH_REPORT_INFO_HIDE_UPPER_LAYER)){
				pTemp->event = TOUCH_EVENT_ABORT;
				/*E Push to "ABORT" queue. */
				pushQueue(&pAbort, &pTemp, 0);
			}else{
				if(abs(pTemp->deltaX) > pEventInfo->moveThreshold ||
				   abs(pTemp->deltaY) > pEventInfo->moveThreshold){
					pTemp->event = TOUCH_EVENT_MOVE;
					/*E Push to "MOVE" queue. */
					pushQueue(&pMove, &pTemp, 0);
				}else{
					pTemp->event = TOUCH_EVENT_HOLD;
					/*E Push to "HOLD" queue. */
					pushQueue(&pHold, &pTemp, 0);
				}
			}

		}else{
			/*E This touch is new one. */
			pTemp = popQueue(&pEventInfo->pFree);
			if(!pTemp){
				return(-1);
			}
			pTemp->id       = pData->report[i].id;
			pTemp->deltaX   = 0;
			pTemp->deltaY   = 0;
			pTemp->deltaF   = 0;
			pTemp->curX     = pData->report[i].x;
			pTemp->curY     = pData->report[i].y;
			pTemp->curF     = pData->report[i].force;
			pTemp->deltaT   = 0;
			pTemp->elapsedT = 0;
			pTemp->pNext    = NULL;

			if((pData->status         & SCE_TOUCH_STATUS_INTERCEPTED) ||
			   (pData->report[i].info & SCE_TOUCH_REPORT_INFO_HIDE_UPPER_LAYER)){
				pTemp->event = TOUCH_EVENT_ABORT;
				/*E Push to "ABORT" queue. */
				pushQueue(&pAbort, &pTemp, 0);
			}else{
				pTemp->event = TOUCH_EVENT_PRESS;
				/*E Push to "PRESS" queue. */
				pushQueue(&pPress, &pTemp, 0);
			}
		}
	}

	/*E This is the "RELEASE" touch. */
	pEventInfo->pRelease = pEventInfo->pHold;
	pTemp = pEventInfo->pRelease;
	while(pTemp){
		if(pData->status & SCE_TOUCH_STATUS_INTERCEPTED){
			pTemp->event = TOUCH_EVENT_ABORT_AND_RELEASE;
		}else{
			if(pTemp->event == TOUCH_EVENT_ABORT){
				pTemp->event = TOUCH_EVENT_ABORT_AND_RELEASE;
			}else{
				pTemp->event = TOUCH_EVENT_RELEASE;
			}
		}
		pTemp->deltaX = 0;
		pTemp->deltaY = 0;
		pTemp->deltaF = 0;
		pTemp->deltaT = 0;
		pTemp         = pTemp->pNext;
	}

	pEventInfo->pMove  = pMove;
	pEventInfo->pHold  = pHold;
	pEventInfo->pPress = pPress;
	pEventInfo->pAbort = pAbort;

	/*E Uptates the time. */
	pEventInfo->updateTime = pData->timeStamp;

	return(0);
}

void touchEventDumpElemQueue(TouchEventElem* pEventElem)
{
	TouchEventElem* pTmp = pEventElem;

	if(pTmp){
		while(pTmp){
			dumpEventElem(pTmp);
			pTmp = pTmp->pNext;
		}
	}
}

